這篇的程式碼在 https://github.com/DanSnow/ironman-2020/tree/master/static-site-generator/packages/vue-ssr
這篇要來實際嘗試 Vue 的 SSR ,這篇做的事很簡單,就是在 server render 好一個 Vue 的程式,然後在 Client 端也把 js 加上去,程式的部份放在 App.vue
中,是個很簡單的 counter ,我們直接來看其它部份,首先我們要準備一個 app.js
,裡面 export 一個函式用來建立 Vue 的程式:
import Vue from 'vue'
import App from './App.vue'
export function createApp() {
const app = new Vue({
render: (h) => h(App),
})
return { app }
}
這邊做最主要的原因是確保每次的 render 都會是新的程式,不會受到之前的狀態影響之類的,如果有需要加上 store 等等的也要加在裡面,目前先不加
再來我們要準備兩個進入點,一個給 server 用來 render html,另一個是在 client 回復程式的狀態時使用,它們分別叫 entry-server.js
與 entry-client.js
,先是 entry-server.js
:
import { createApp } from './app'
export default (context) => {
const { app } = createApp()
return app
}
目前就很簡單的把 createApp
包裝一下再 export 出去而已,再來是 entry-client.js
,這其實跟平常寫 Vue 沒什麼兩樣:
import { createApp } from './app'
const { app } = createApp()
app.$mount('#root')
再來要準備 webpack 的設定打包這兩個檔案,我們來寫個 webpack.config.js
,另外這篇用的是 webpack 5 ,原生就支援 PnP 感覺很方便:
const webpack = require('webpack')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const baseConfig = {
module: {
rules: [
// vue loader 的設定
{
test: /\.vue$/,
loader: 'vue-loader',
},
],
},
plugins: [
new VueLoaderPlugin(),
// webpack 5 預設不再提供 node 的 API 了
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
}),
],
optimization: {
// 開一下串接模組,這樣產出來的檔案會小一點,比較好看
// 如果全都是 es6 module , webpack 5 甚至有可能編出不含 runtime 的 code 喔
concatenateModules: true,
},
mode: 'none',
devtool: false,
}
module.exports = [
// server 的設定
{
...baseConfig,
entry: {
server: './src/entry-server',
},
output: {
// 要把 app export 出來,所以這邊要設定 `libraryTarget`
libraryTarget: 'commonjs2',
},
// 不需要打包進 vue
externals: ['vue'],
// 一定要指明是 `node` ,這同時會讓 vue-loader 編出 SSR 用的 code
target: 'node',
},
// client 的設定
{
...baseConfig,
entry: {
client: './src/entry-client',
},
},
]
寫完後就直接輸入 yarn webpack
跑一下打包吧,最後是 server.js
:
const { resolve } = require('path')
const express = require('express')
const { createRenderer } = require('vue-server-renderer')
const createApp = require('./dist/server').default
// vue-ssr-outlet 是 vue 的 renderer 會插入 render 好的 html 的位置
const template = `
<html>
<head>
<title>SSR test</title>
</head>
<body>
<!--vue-ssr-outlet-->
<script src="client.js"></script>
</body>
</html>
`
const app = express()
const renderer = createRenderer({
template,
})
app.use(express.static(resolve(__dirname, 'dist')))
app.get('/', async (req, res) => {
const context = {}
const app = createApp()
// 第一個參數是一個 vue 的 instance ,第二個則是一個 object ,回傳是一個 promise 包著 html
const html = await renderer.renderToString(app, context)
res.send(html)
})
app.listen(3000, () => {
console.log('listen at http://localhost:3000')
})
就這樣,可以在終端機中輸入 yarn node server.js
試試了,應該可以在 http://localhost:3000 看到我們的網頁,到這邊基本的東西準備完了,下一篇我們再來把 vue-router 跟 Vuex 加進去,也順便試一下 serverPrefetch
跟 bundle renderer 吧
感謝分享
補充 new Vue() 是 Vue 2 語法,
Vue 3 用 Vue.createApp() 取代 new Vue()
https://book.vue.tw/appendix/migration.html#%E5%85%83%E4%BB%B6%E5%AF%A6%E9%AB%94%E5%BB%BA%E7%AB%8B
Vue 2 support will end on Dec 31, 2023. Learn more about Vue 2 Extended LTS.
The Benefits of the New Vue 3 App Initialization Code